package com.common.data.desensitization.common.aspect;

import com.common.data.desensitization.common.annotation.ConditionMatchStrategy;
import com.common.data.desensitization.common.annotation.DesensitizationRule;
import com.common.data.desensitization.common.annotation.Desensitize;
import com.common.data.desensitization.common.enums.DesensitizationStrategyEnum;
import com.common.data.desensitization.common.utils.DesensitizationServiceUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Aspect
@Component
public class DesensitizationAspect {
    private static final Logger logger = LoggerFactory.getLogger(DesensitizationAspect.class);

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private DesensitizationServiceUtil desensitizationService;

    // 防止各种 jsonpath 配置错误 不抛出异常返回空
    private final Configuration jsonPathConfig = Configuration.defaultConfiguration()
            .addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL)
            .addOptions(Option.SUPPRESS_EXCEPTIONS)
            .addOptions(Option.ALWAYS_RETURN_LIST);

    // SpEL 解析器
    private final ExpressionParser expressionParser = new SpelExpressionParser();
    //  参数名发现器--获取方法参数名
    private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
    // 用于匹配占位符，例如 #userId, #p0, #body.fieldName
    private static final Pattern ARG_PLACEHOLDER_PATTERN = Pattern.compile("#([a-zA-Z0-9_\\.]+)");
    @Around("@within(org.springframework.web.bind.annotation.RestController) && @annotation(desensitizeAnnotation)")
    public Object around(ProceedingJoinPoint pjp, Desensitize desensitizeAnnotation) throws Throwable {
        Object result = pjp.proceed();

        if (result == null) {
            return null;
        }

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Object[] args = pjp.getArgs(); // 获取方法参数

        // 预处理规则中的 jsonPath 和 conditionJsonPath，替换占位符
        DesensitizationRule[] processedRules = processRulesWithArguments(desensitizeAnnotation.rules(), method, args);


        // 如果返回的是基本类型或String，直接返回
        if (result instanceof String || result instanceof Number || result instanceof Boolean) {
            //对于String，只有一个规则且jsonPath为$，条件判断也可能需要参数
            if (processedRules.length == 1 && "$".equals(processedRules[0].jsonPath())) {
                DesensitizationRule rule = processedRules[0];
                if (isConditionMet(result, rule, null)) {
                    return desensitizationService.desensitizeField((String) result, rule);
                }
            }
            // 对于其他基本类型或不匹配的String，直接返回
            return result;
        }


        long startTime = System.currentTimeMillis();
        try {
            String jsonResult = objectMapper.writeValueAsString(result);
            DocumentContext documentContext = JsonPath.parse(jsonResult, jsonPathConfig);

            for (DesensitizationRule rule : processedRules) { // 使用处理过的规则
                if (!isConditionMet(null, rule, documentContext)) {
                    continue;
                }
                String finalJsonPath = rule.jsonPath();
                try {
                    List<Object> currentValues = documentContext.read(finalJsonPath);
                    if (!CollectionUtils.isEmpty(currentValues)) {
                        documentContext.map(finalJsonPath, (currentValue, conf) -> {
                            if (currentValue instanceof String) {
                                return desensitizationService.desensitizeField((String) currentValue, rule);
                            }
                            return currentValue;
                        });
                    }
                } catch (Exception e) {
                    logger.error("脱敏发生异常路径是: {} 错误信息是: {}", finalJsonPath, e.getMessage(), e);
                }
            }

            Class<?> returnType = method.getReturnType();
            if (returnType == void.class || returnType == Void.class) {
                return null;
            }

            // 转换回原始对象类型
            if (Map.class.isAssignableFrom(returnType) || List.class.isAssignableFrom(returnType) || returnType.isArray()) {
                return objectMapper.readValue(documentContext.jsonString(), returnType);
            } else if (result.getClass().equals(returnType)) {
                return objectMapper.readValue(documentContext.jsonString(), result.getClass());
            } else {
                logger.warn("返回类型不匹配，正在尝试 result.getClass(). 获取返回值类型: {}, 返回值类型是: {}", returnType, result.getClass());
                return objectMapper.readValue(documentContext.jsonString(), result.getClass());
            }
        } catch (Exception e) {
            logger.error("脱敏AOP切面发生异常: {}", e.getMessage(), e);
            return result;
        } finally {
            logger.info("脱敏AOP斜面耗时: {} ms", (System.currentTimeMillis() - startTime));
        }
    }

    /**
     * 处理规则数组，用方法参数替换jsonPath和conditionJsonPath中的占位符
     */
    private DesensitizationRule[] processRulesWithArguments(DesensitizationRule[] originalRules, Method method, Object[] args) {
        if (args == null || args.length == 0) { // 没有参数，无需处理
            return originalRules;
        }
        DesensitizationRule[] processedRules = new DesensitizationRule[originalRules.length];
        StandardEvaluationContext context = createEvaluationContext(method, args);

        for (int i = 0; i < originalRules.length; i++) {
            DesensitizationRule originalRule = originalRules[i];
            String processedJsonPath = replacePlaceholders(originalRule.jsonPath(), context);
            String processedConditionJsonPath = replacePlaceholders(originalRule.conditionJsonPath(), context);
            String processedConditionValue = replacePlaceholders(originalRule.conditionValue(), context); // 条件值也可能需要动态

            // 注解是接口，不能直接修改，需要新对象
            final DesensitizationRule currentOriginalRule = originalRule;
            final String finalProcessedJsonPath = processedJsonPath;
            final String finalProcessedConditionJsonPath = processedConditionJsonPath;
            final String finalProcessedConditionValue = processedConditionValue;

            processedRules[i] = new DesensitizationRule() {
                @Override public String jsonPath() { return finalProcessedJsonPath; }
                @Override public DesensitizationStrategyEnum strategy() { return currentOriginalRule.strategy(); }
                @Override public char maskChar() { return currentOriginalRule.maskChar(); }
                @Override public int prefixKeep() { return currentOriginalRule.prefixKeep(); }
                @Override public int suffixKeep() { return currentOriginalRule.suffixKeep(); }
                @Override public String middleReplaceString() { return currentOriginalRule.middleReplaceString(); }
                @Override public String regex() { return currentOriginalRule.regex(); }
                @Override public String replacement() { return currentOriginalRule.replacement(); }
                @Override public Class<? extends com.common.data.desensitization.common.handler.DesensitizationHandler> customHandler() { return currentOriginalRule.customHandler(); }
                @Override public String conditionJsonPath() { return finalProcessedConditionJsonPath; }
                @Override public String conditionValue() { return finalProcessedConditionValue; }
                @Override public Class<? extends java.lang.annotation.Annotation> annotationType() { return DesensitizationRule.class; }

                @Override public ConditionMatchStrategy conditionMatchStrategy() { return currentOriginalRule.conditionMatchStrategy(); }

            };
        }
        return processedRules;
    }

    /**
 * 替换字符串中的 SpEL 占位符
     */
    private String replacePlaceholders(String pathWithPlaceholders, StandardEvaluationContext context) {
        if (!StringUtils.hasText(pathWithPlaceholders) || pathWithPlaceholders.indexOf('#') == -1) {
            return pathWithPlaceholders;
        }

        StringBuffer sb = new StringBuffer();
        Matcher matcher = ARG_PLACEHOLDER_PATTERN.matcher(pathWithPlaceholders);
        while (matcher.find()) {
            String placeholderExpression = matcher.group(1); // 获取占位符表达式，如 userId, p0, body.name
            try {
                // SpEL表达式求值
                Object value = expressionParser.parseExpression("#" + placeholderExpression).getValue(context);
                String replacementValue;
                if (value instanceof String) {
                    replacementValue = "'" + ((String) value).replace("'", "''") + "'";
                } else if (value == null) {
                    replacementValue = "null";
                } else {
                    replacementValue = String.valueOf(value);
                }
                matcher.appendReplacement(sb, Matcher.quoteReplacement(replacementValue));
            } catch (Exception e) {
                logger.warn("无法解析占位符表达式: {} 在路径中: {}. 错误: {}", placeholderExpression, pathWithPlaceholders, e.getMessage());
                matcher.appendReplacement(sb, Matcher.quoteReplacement(matcher.group(0))); // 保留原始占位符
            }
        }
        matcher.appendTail(sb);
        logger.debug("路径占位符替换: '{}' -> '{}'", pathWithPlaceholders, sb.toString());
        return sb.toString();
    }


    /**
     * 创建SpEL上下文
     *
     * @param method
     * @param args
     * @return StandardEvaluationContext
     */
    private StandardEvaluationContext createEvaluationContext(Method method, Object[] args) {
        StandardEvaluationContext context = new StandardEvaluationContext();

        String[] paramNames = parameterNameDiscoverer.getParameterNames(method);
        Annotation[][] paramAnnotations = method.getParameterAnnotations(); // 获取参数的注解

        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]); // 按名称设置变量
                context.setVariable("p" + i, args[i]);      // 按索引设置变量

                // 检查参数是否有 @RequestBody 注解
                boolean isRequestBody = false;
                for (Annotation annotation : paramAnnotations[i]) {
                    if (annotation instanceof RequestBody) {
                        isRequestBody = true;
                        break;
                    }
                }

                if (isRequestBody && args[i] != null) {
                    // 如果是 @RequestBody 参数，将其注册到上下文中，命名为 "body" ，这样就可以通过 #body.propertyName 来访问其属性
                    // 规范一下（没啥用，显得专业）
                    context.setVariable("body", args[i]);
                    logger.debug("在SpEL上下文中将@RequestBody参数 '{}' (类型是: {}) 重命名为body,请用#body.paramName获取", paramNames[i], args[i].getClass().getSimpleName());
                }
            }
        }
        return context;
    }




    /**
     *  判断脱敏规则的条件是否满足
     *
     * @param directValue
     * @param rule
     * @param documentContext
     * @return boolean
     **/
    private boolean isConditionMet(Object directValue, DesensitizationRule rule, DocumentContext documentContext) {
        String conditionPath = rule.conditionJsonPath();
        String conditionExpectedValue = rule.conditionValue();
        ConditionMatchStrategy conditionMatchStrategy = rule.conditionMatchStrategy();
        if (!StringUtils.hasText(conditionPath) || !StringUtils.hasText(conditionExpectedValue)) {
            return true;
        }

        // 对期望值进行预处理，去除可能的引号（如果它是字面量）
        String processedExpectedValue = conditionExpectedValue;
        if ((processedExpectedValue.startsWith("'") && processedExpectedValue.endsWith("'")) ||
                (processedExpectedValue.startsWith("\"") && processedExpectedValue.endsWith("\""))) {
            if (processedExpectedValue.length() > 1) {
                processedExpectedValue = processedExpectedValue.substring(1, processedExpectedValue.length() - 1);
            }
        }
        if (documentContext != null) {
            try {
                List<Object> actualValuesFromPath = documentContext.read(conditionPath);
                if (CollectionUtils.isEmpty(actualValuesFromPath)) {
                    // 如果路径读取结果为空列表，但期望值是 "null" 或空字符串，根据业务判断
                    // 这里简单处理为：路径没读到值，条件不满足（除非期望值也是某种形式的“空”）
                    if ("null".equalsIgnoreCase(processedExpectedValue) && actualValuesFromPath.isEmpty()) {
                        // 这种情况比较微妙，如果jsonpath没找到任何节点，但期望是null，找不到节点视为不匹配。
                        return false;
                    }
                    // 如果期望值是空字符串，且 actualValuesFromPath 包含一个空字符串，下面会匹配到
                    return false; // 路径未读取到任何值
                }
                // 采用 ANY 逻辑: 只要列表中有一个值匹配期望值，条件就满足
                if(conditionMatchStrategy == ConditionMatchStrategy.ANY){
                    for (Object actualValue : actualValuesFromPath) {
                        if (actualValue == null) {
                            if ("null".equalsIgnoreCase(processedExpectedValue)) {
                                logger.debug("满足条件：路径的实际（null）与预期（null）匹配conditionPath: '{}'", conditionPath);
                                return true; // 实际为null，期望为"null"字符串
                            }
                            // 如果 actualValue 是 null 但 processedExpectedValue 不是 "null"，则此项不匹配，继续看下一个
                            continue;
                        }
                        // 类型可能不匹配，统一转为String比较
                        if (processedExpectedValue.equals(String.valueOf(actualValue))) {
                            logger.debug("满足条件：实际 ('{}') 预期匹配: ('{}') path是： '{}'", actualValue, processedExpectedValue, conditionPath);
                            return true;
                        }
                    }
                    logger.debug("不满足条件：路径中的列表中没有值 '{}' 匹配的预期值 '{}'", conditionPath, processedExpectedValue);
                    return false; // 列表中没有任何值匹配
                }else if(conditionMatchStrategy == ConditionMatchStrategy.ALL){
                    for (Object actualValue : actualValuesFromPath) {
                        boolean currentMatch = false;
                        if (actualValue == null) {
                            if ("null".equalsIgnoreCase(processedExpectedValue)) {
                                currentMatch = true;
                            }
                        } else if (processedExpectedValue.equals(String.valueOf(actualValue))) {
                            currentMatch = true;
                        }

                        if (!currentMatch) {
                            logger.debug("条件（ALL）未满足：值 '{}' path是: '{}' 与预期值不匹配 :'{}'", actualValue, conditionPath, processedExpectedValue);
                            return false; // 只要有一个不匹配，ALL 就失败
                        }
                    }
                    logger.debug("满足条件（ALL）：路径中的所有值:'{}'匹配的预期值 '{}'", conditionPath, processedExpectedValue);
                    return true; // 所有都匹配了
                }
            } catch (Exception e) {
                logger.warn("读取条件JsonPath '{}' 失败或条件检查时出错: {}", conditionPath, e.getMessage());
                return false; // 条件路径读取失败或比较出错，视为不满足
            }
        } else if (directValue != null && "$".equals(conditionPath)) { // 针对直接对String进行判断的场景
            if (directValue == null) {
                return "null".equalsIgnoreCase(processedExpectedValue);
            }
            return processedExpectedValue.equals(String.valueOf(directValue));
        }
        return false;
    }

}
